package com.geline.easytrans.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.geline.cloud.util.DynamicSqlUtil;
import com.geline.easytrans.annotation.*;
import com.geline.easytrans.config.DictEnumRegistry;
import com.geline.easytrans.handler.TransDictHandler;
import com.geline.easytrans.handler.TransFieldHandler;
import com.geline.easytrans.handler.TransSQLHandler;
import com.geline.easytrans.util.ExpressionParserUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 子查询service
 *  处理 @TransDict(dictType="字典类型code", source = "来源属性")
 *  处理 @TransTable(table="", foreignKey = "")
 *  处理 @TransSQL(value="动态查询SQL")
 *  处理 @TransEntity(handler="TransEntityHandler实现类")
 *
 * @author: mengxin
 * @date: 2023/7/23 21:43
 */
@Slf4j
public class EasyTransService {

    private TransDictHandler dictHandler;
    private TransSQLHandler transSQLHandler;
    private static final String userIdKey = "userId";

    public EasyTransService(TransDictHandler dictHandler, TransSQLHandler sqlHandler){
        this.dictHandler = dictHandler;
        this.transSQLHandler = sqlHandler;
    }

    public void transOne(Object result){
        this.transOne(result, null, false);
    }

    public void transOne(Object result, boolean onlyBlankField){
        this.transOne(result, null, onlyBlankField);
    }

    public void transOne(Object result, Object userId, boolean onlyBlankField){
        this.transOne(result, userId, null, onlyBlankField);
    }

    public void transOne(Object result, List<String> keys, boolean onlyBlankField){
        this.transOne(result, null, keys, onlyBlankField);
    }

    public void transOne(Object result, Object userId, List<String> keys, boolean onlyBlankField){
        transOne(result, userId, keys, onlyBlankField, 1, 4);
    }

    /**
     * 处理Entity @TransDict @TransEnum @TransSQL @TransEntity
     * @param result
     * @param userId 用户ID
     * @param onlyBlankField 【true:只翻译为空的属性，false:翻译所有@TransSQL属性】
     * @param currLevel 当前属性层级
     * @param maxLevel 翻译属性的最大层级：默认翻译3级，防止循环翻译对象
     */
    public void transOne(Object result, Object userId, List<String> keys, boolean onlyBlankField, Integer currLevel, Integer maxLevel) {
        if(currLevel > maxLevel){
            log.warn("翻译属性层级超过最大层级，不再翻译！");
        }
        Class<?> entityClass = result.getClass();
        List<Field> fieldList = new ArrayList<>();
        Field[] fields = ReflectUtil.getFields(entityClass);
        if(keys!=null && !keys.isEmpty()){
            for(Field field : fields){
                if(keys.contains(field.getName())){
                    fieldList.add(field);
                }
            }
        }else {
            fieldList = Arrays.asList(fields);
        }
        for(Field field : fieldList){
            //处理 @TransDict
            TransDict transDict = field.getAnnotation(TransDict.class);
            if(transDict!=null){
                this.handleTransDict(transDict, result, field);
                log.debug("over handleTransDict = {}", field.getName());
                continue;
            }
            //处理 @TransTable
            TransTable transTable = field.getAnnotation(TransTable.class);
            if(transTable!=null){//已经有值不用处理
                Object value = BeanMap.create(result).get(field.getName());
                log.debug("@TransTable field={}, value={}", field.getName(), value);
                if(onlyBlankField && StrUtil.isBlankIfStr(value)){
                    this.handleTransTable(transTable, result, field, userId, keys, onlyBlankField);
                }else if(!onlyBlankField){
                    this.handleTransTable(transTable, result, field, userId, keys, onlyBlankField);
                }
                continue;
            }
            //处理 @TransSQL
            TransSQL transSQL = field.getAnnotation(TransSQL.class);
            if(transSQL!=null){//已经有值不用处理
                Object value = BeanMap.create(result).get(field.getName());
                log.debug("find @TransSQL : {} = {}", field.getName(), value);
                if(onlyBlankField && StrUtil.isBlankIfStr(value)){
                    this.handleTransSQL(transSQL, result, field, userId, keys, onlyBlankField);
                }else if(!onlyBlankField){
                    this.handleTransSQL(transSQL, result, field, userId, keys, onlyBlankField);
                }
                continue;
            }
            //处理 @TransEnum
            TransEnum transEnum = field.getAnnotation(TransEnum.class);
            if(transEnum!=null){
                this.handleTransEnum(transEnum, result, field);
                //log.debug("over handleTransEnum = {}", field.getName());
                continue;
            }
            //处理 @TransEntity
            TransField transField = field.getAnnotation(TransField.class);
            if(transField !=null){
                this.handleTransEntity(transField, result, field);
                continue;
            }

            Object value = BeanMap.create(result).get(field.getName());
            if(value==null){
                continue;
            }

            if(isCustomBean(value)){
                //自定义Bean
                transOne(value, onlyBlankField);
            }else if(value instanceof Collection){
                //Collection<Bean> >>> List<Bean> or Set<Bean>
                Collection<?> list = (Collection<?>) value;
                if(!list.isEmpty() && isCustomBean(list.stream().findFirst().get())){
                    list.forEach(row -> transOne(row, userId, keys, onlyBlankField, currLevel+1, maxLevel));
                }
            }else if(value.getClass().isArray()){
                //Bean[]
                List<Object> list = Arrays.asList(value);
                if(!list.isEmpty() && isCustomBean(list.stream().findFirst().get())){
                    list.forEach(row -> transOne(row, userId, keys, onlyBlankField, currLevel+1, maxLevel));
                }
            }
        }
    }

    // 判断对象是否为自定义 Bean 类型
    private boolean isCustomBean(Object obj) {
        if (obj == null) return false;
        Class<?> clazz = obj.getClass();

        // 排除基本类型、数组、枚举、注解等
        if (!ClassUtil.isNormalClass(clazz)) {
            return false;
        }

        // 获取类的包名
        Package pkg = clazz.getPackage();
        if (pkg == null) return false;

        String packageName = pkg.getName();
        // 排除 Java 系统包、Hutool 包、Spring 框架包等
        return !(packageName.startsWith("java.") ||
                packageName.startsWith("javax.") ||
                packageName.startsWith("com.sun.") ||
                packageName.startsWith("cn.hutool.") ||
                packageName.startsWith("org.springframework."));
    }

    public void transBatch(Collection list){
        transBatch(list, null, null, false);
    }

    public void transBatch(Collection list, boolean onlyBlankField){
        transBatch(list, null, null, onlyBlankField);
    }

    public void transBatch(Collection list, Object userId, boolean onlyBlankField){
        transBatch(list, userId, null, onlyBlankField);
    }

    /**
     * 处理List @TransSQL
     * @param list
     */
    public void transBatch(Collection list, Object userId, List<String> keys, boolean onlyBlankField){
        if(list!=null && !list.isEmpty()){
            list.forEach(row -> transOne(row, userId, keys, onlyBlankField));
        }
    }

    //支持基础类型，Entity类型
    public <T> T getOneBySql(String dynamicSql, Map<String, Object> example, Object userId, Class<T> clazz){
        Map<String, Object> map = new HashMap<>();
        map.put(userIdKey, userId);
        map.putAll(example);
        String newSql = DynamicSqlUtil.parseSQL(dynamicSql, map);
        //存在变量时不执行查询
        if(newSql.contains("#{") || newSql.contains("${")){
            log.warn("fail execute sql = {}", newSql);
            log.debug("fail map = {}", map);
            return null;
        }
        return transSQLHandler.selectOne(newSql, clazz);
    }

    public Map<String, Object> getMapBySql(String dynamicSql, Map<String, Object> example, Object userId){
        Map<String, Object> map = new HashMap<>();
        map.put(userIdKey, userId);
        map.putAll(example);
        String newSql = DynamicSqlUtil.parseSQL(dynamicSql, map);
        //存在变量时不执行查询
        if(newSql.contains("#{") || newSql.contains("${")){
            log.warn("can not execute sql = {}", newSql);
            log.debug("map = {}", map);
            return null;
        }
        return transSQLHandler.selectOne(newSql, Map.class);
    }

    public List<Map> listMapsBySql(String dynamicSql, Map<String, Object> example, Object userId){
        Map<String, Object> map = new HashMap<>();
        map.put(userIdKey, userId);
        map.putAll(example);
        String newSql = DynamicSqlUtil.parseSQL(dynamicSql, map);
        //存在变量时不执行查询
        if(newSql.contains("#{") || newSql.contains("${")){
            log.warn("list can not execute sql = {}", newSql);
            log.info("list map = {}", map);
            return null;
        }
        return transSQLHandler.selectList(newSql, Map.class);
    }

    /**
     * 处理 @TransDict
     *
     * @param result
     * @param field
     * @return
     */
    private void handleTransDict(TransDict annotation, Object result, Field field) {
        Object value = BeanMap.create(result).get(annotation.source());
        if(!StrUtil.isBlankIfStr(value)){
            // value -> label
            String dictLabel = dictHandler.getDictLabel(annotation.dictType(), value);
            if(dictLabel != null){
                BeanUtil.setFieldValue(result, field.getName(), dictLabel);
            }else {
                // label -> value
                Object dictValue = dictHandler.getDictValue(annotation.dictType(), value.toString());
                Assert.notNull(dictValue, String.format("can not find dictType : %s !)", annotation.dictType()));
                BeanUtil.setFieldValue(result, field.getName(), dictValue);
            }
        }
    }

    /**
     * 处理 @TransEnum
     *
     * @param result
     * @param field
     * @return
     */
    private void handleTransEnum(TransEnum annotation, Object result, Field field) {
        Object value = BeanMap.create(result).get(annotation.source());
        if(!StrUtil.isBlankIfStr(value)){
            String dictLabel = DictEnumRegistry.getDictLabel(annotation.dictType(), value);
            if(dictLabel != null){
                BeanUtil.setFieldValue(result, field.getName(), dictLabel);
            }else {
                Object dictValue = DictEnumRegistry.getDictValue(annotation.dictType(), value.toString());
                Assert.notNull(dictValue, String.format("can not find @DictType(value = \"%s\")", annotation.dictType()));
                BeanUtil.setFieldValue(result, field.getName(), dictValue);
            }
        }
    }

    private void handleTransTable(TransTable annotation, Object result, Field field, Object userId, List<String> keys, boolean onlyBlankField) {
        String condition = annotation.condition();
        if(StrUtil.isNotBlank(condition)){
            Boolean execute = ExpressionParserUtils.executeSpEL(result, condition);
            if(!execute){
                log.debug("@TransTable executeSpEL:{} = false", condition);
                return;
            }
        }

        Class<?> typeClass = field.getType();
        String dynamicSql = String.format("select * from %s where %s=#{%s}", annotation.table(), annotation.foreignKey(), annotation.idField());
        Map<String, Object> example = BeanMap.create(result);
        if(typeClass.isArray() || typeClass==List.class){
            String newSql = DynamicSqlUtil.parseSQL(dynamicSql, example);
            //存在变量时不执行查询
            if(newSql.indexOf("#{")!=-1 || newSql.indexOf("${")!=-1){
                log.warn("@TransTable = {}", newSql);
                log.debug("@TransTable example = {}", example);
                return;
            }
            Class<?> rowClass = Map.class;
            Type genericType = field.getGenericType();
            if (genericType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericType;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                rowClass = (Class<?>) actualTypeArguments[0];
            }
            List beanList = transSQLHandler.selectList(newSql, rowClass);
            if(beanList!=null && beanList.size()>0){
                BeanUtil.setFieldValue(result, field.getName(), beanList);
                //子属性为list时，继续翻译List每一个子对象
                transBatch(beanList, userId, keys, onlyBlankField);
            }else {
                if(typeClass==List.class){
                    BeanUtil.setFieldValue(result, field.getName(), new ArrayList<>());
                }
            }
        }else{
            //原始类型 Integer, Long, String, Date, Number, Enum, LocalDateTime...
            if(ClassUtil.isSimpleValueType(typeClass)){
                Map<String, Object> value = this.getMapBySql(dynamicSql, example, userId);
                if(value!=null){
                    //如果是查询对象，赋值给基础类型时，允许扩展赋值给其它存在的字段:如返回User对象，同时赋值给id,userName基础字段属性
                    BeanMap.create(result).putAll(value);
                }
            }else {
                //其它类型 Map, Entity
                Object value = this.getOneBySql(dynamicSql, example, userId, typeClass);
                if(value!=null){
                    BeanUtil.setFieldValue(result, field.getName(), value);
                    //子属性为Bean时，继续翻译Bean每一个子属性
                    transOne(value, userId, keys, onlyBlankField);
                }
            }
        }
    }

    private void handleTransSQL(TransSQL annotation, Object result, Field field, Object userId, List<String> keys, boolean onlyBlankField) {
        //存在属性值时才执行查询
        String condition = annotation.condition();
        if(StrUtil.isNotBlank(condition)){
            Boolean execute = ExpressionParserUtils.executeSpEL(result, condition);
            if(!execute){
                log.debug("@TransSQL({})=false", condition);
                return;
            }
        }

        Class<?> typeClass = field.getType();
        String dynamicSql = annotation.value();
        if(StrUtil.isBlank(dynamicSql)){
            String sqlIdInMapper = annotation.sqlIdInMapper();
            log.debug("sqlIdInMapper = {}", sqlIdInMapper);
            if(StrUtil.isNotBlank(sqlIdInMapper)){
                dynamicSql = transSQLHandler.getSql(sqlIdInMapper);
            }
        }

        if(StrUtil.isBlank(dynamicSql)){
            throw new RuntimeException(field.getName()+" @TransSQL [value or sqlIdInMapper] is required !");
        }

        Map<String, Object> example = BeanMap.create(result);
        example.put(userIdKey, userId);
        if(typeClass.isArray() || typeClass==List.class){
            String newSql = DynamicSqlUtil.parseSQL(dynamicSql, example);
            //存在变量时不执行查询
            if(newSql.indexOf("#{")!=-1 || newSql.indexOf("${")!=-1){
                log.warn("@TransSQL = {}", newSql);
                log.debug("@TransSQL example = {}", example);
                return;
            }
            Class<?> rowClass = Map.class;
            Type genericType = field.getGenericType();
            if (genericType instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) genericType;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                rowClass = (Class<?>) actualTypeArguments[0];
            }
            List beanList = transSQLHandler.selectList(newSql, rowClass);
            if(beanList!=null && !beanList.isEmpty()){
                BeanUtil.setFieldValue(result, field.getName(), beanList);
                //子属性为list时，继续翻译List每一个子对象
                transBatch(beanList, userId, keys, onlyBlankField);
            }else {
                if(typeClass==List.class){
                    BeanUtil.setFieldValue(result, field.getName(), new ArrayList<>());
                }else {
                    log.warn("unSupport setFieldValue typeClass = {}", typeClass);
                }
            }
        }else if(ClassUtil.isSimpleValueType(typeClass)){
            log.debug("@TransSQL isSimpleValueType = {}", typeClass);
            //原始类型 Integer, Long, String, Date, Number, Enum, LocalDateTime...
            String lowerCaseSql = dynamicSql.toLowerCase();
            int index = lowerCaseSql.indexOf("from");
            if(index==-1){
                log.error("fail @TransSQL = {}", dynamicSql);
            }else {
                String preSql = dynamicSql.substring(0, index);
                if(preSql.indexOf(",")!=-1){
                    //查询多个字段
                    Object object = this.getOneBySql(dynamicSql, example, userId, result.getClass());
                    Map<String, Object> beanMap = BeanMap.create(object);
                    for(String key : beanMap.keySet()){
                        Object value = beanMap.get(key);
                        if(value!=null){
                            BeanUtil.setFieldValue(result, key, value);
                        }
                    }
                }else {
                    Object value = this.getOneBySql(dynamicSql, example, userId, typeClass);
                    BeanUtil.setFieldValue(result, field.getName(), value);
                }
                String defValue = annotation.defaultValue();
                Object fieldValue = BeanMap.create(result).get(field.getName());
                if(fieldValue==null && StrUtil.isNotBlank(defValue)){
                    BeanWrapper wrapper = new BeanWrapperImpl(result);
                    wrapper.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
                    wrapper.setPropertyValue(field.getName(), defValue);
                }
            }
        }else{
            //对象类型：Map, Entity
            Object value = this.getOneBySql(dynamicSql, example, userId, typeClass);
            if(value!=null){
                BeanUtil.setFieldValue(result, field.getName(), value);
                if(isCustomBean(value)){
                    //子属性为Bean时，继续翻译Bean每一个子属性
                    transOne(value, userId, keys, onlyBlankField);
                }
            }
        }
    }

    private void handleTransEntity(TransField annotation, Object result, Field field) {
        //存在属性值时才执行查询
        String condition = annotation.condition();
        if(StrUtil.isNotBlank(condition)){
            Boolean execute = ExpressionParserUtils.executeSpEL(result, condition);
            if(!execute){
                log.debug("@TransField executeSpEL:{} = false", condition);
                return;
            }
        }

        Class<? extends TransFieldHandler> handler = annotation.handler();
        TransFieldHandler transService = handler != null ? SpringUtil.getBean(handler) : SpringUtil.getBean(TransFieldHandler.class);
        if(transService != null){
            transService.handle(annotation.catalog(), result, field);
        }
    }
}
